package aparapi;

import ex.ArrayHandler;
import ex.DiffSetPair;
import org.apache.commons.math3.util.Combinations;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.Scanner;

/**
 * @Description 分段用Aparapi搜索
 * @Date 2022/3/27 16:20
 * @Author eric ling
 */
public class PartAparSearch {
    /*
   边界验证：nCk 当k=0时
   DSP(13,6,6,0,3) D1={1,3,9,4,12,10} D2={2,6,5,7,8,11}
   DSP(17,8,8,0,4) D1={1,2,4,8,16,15,13,9} D2={3,6,12,7,14,11,5,10}

   小范围
   DSP(15,3,5,1,1) D1={0,5,10} D2={0,3,6,9,12}
   DSP(13,6,7,6,3) D={1,3,4,9,10,12} D2={1,3,4,9,10,12,0}

   大范围
   DSP(31,11,6,6,2)
   DSP(51,26,16,16,8)
   DSP(57,29,20,20,10)
   DSP(69,35,24,24,12)
   DSP(73,54,19,18,14)

   构造DSP
   DSP(4v+1,2v,2v,0,v)
   DSP(4v+1,2v+1,2v,2v,v)
*/
    public static void main(String[] args) {

        System.out.println("请输入系统变量v,k1,k2,e,lambda，例如：15,3,5,1,1");
        Scanner scanner = new Scanner(System.in);
        String[] numStrs = scanner.nextLine().split("[,;\\s]+");
        int[] nums = new int[5];
        int tmpIdx = 0;
        for (String numStr : numStrs) {
            nums[tmpIdx++] = Integer.parseInt(numStr);
        }

        // 利用必要条件预筛选给定差集偶的参数
        while (!DiffSetPair.isValid(nums)) {
            // 给定参数不合法
            System.out.println("给定参数不存在差集偶，请重新输入：");
            numStrs = scanner.nextLine().split("[,;\\s]+");
            tmpIdx = 0;
            for (String numStr : numStrs) {
                nums[tmpIdx++] = Integer.parseInt(numStr);
            }
        }
        System.out.println("searching...");
        File file = new File("result_" + Arrays.toString(nums) + "_" + LocalDate.now() + "_partAparSearch.txt");
        if (!file.exists()) {
            try {
                System.out.println("create file " + file.getAbsolutePath() + ":" + file.createNewFile());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        DiffSetPair p = new DiffSetPair(nums);
        long start = System.currentTimeMillis();
        try (FileWriter writer = new FileWriter(file)) {
            writer.write("DSP(" + p.getV() + ", " + p.getK1() + ", " + p.getK2() + ", " + p.getE() + ", " + p.getLambda() + "):\n");
            System.out.println("共搜索到" + run(p, writer) + "个满足给定参数的差集偶");
        } catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("搜索耗时：" + (end - start) + "毫秒");
        System.out.println("差集偶搜索完毕，结果保存在" + file.getAbsolutePath());
    }

    public static int run(DiffSetPair p, Writer writer) throws IOException {
        int found = 0;
        // 所有的数从该集合中选取
        boolean[] remainderSetIdx = new boolean[p.getV()]; // 用下标表示集合元素，存放的boolean表示是否存在集合中
        Arrays.fill(remainderSetIdx, true);

        final int MAX_VALUE = 2345678;
        byte[] k1SetAllComb = new byte[MAX_VALUE * p.getK1()];
        byte[] k2SetAllComb = new byte[MAX_VALUE * p.getK2()];
        int combIdx = 0;
        if (p.getE() == 0) {
            // nCk k为0 额外的处理逻辑
            // 开始迭代k2Set
            for (int[] k2SetIdx : new Combinations(p.getV(), p.getK2())) {
                boolean[] remainderSetIdx1 = remainderSetIdx.clone();
                byte[] k2Set = ArrayHandler.extractCombElementB(remainderSetIdx1, k2SetIdx);
                // 确定集合k2Set
                // p.setK2Set(k2Set);

                // 开始迭代k1Set
                for (int[] k1SetIdx : new Combinations(p.getV() - p.getK2(), p.getK1())) {
                    boolean[] remainderSetIdx2 = remainderSetIdx1.clone();
                    byte[] k1Set = ArrayHandler.extractCombElementB(remainderSetIdx2, k1SetIdx);
                    // 确定集合k1Set
                    // p.setK1Set(k1Set);
                    // 将当前组合的k1Set和k2Set填充到k1SetAllComb和k2SetAllComb中
                    ArrayHandler.fillToArray(k1SetAllComb, k1Set, combIdx);
                    ArrayHandler.fillToArray(k2SetAllComb, k2Set, combIdx);
                    combIdx++;

                    // 开始分段
                    if (combIdx == MAX_VALUE) {
                        combIdx = 0; // 归零
                        DspKernel dspKernel = new DspKernel(k1SetAllComb, k2SetAllComb, p, MAX_VALUE);
                        dspKernel.execute(MAX_VALUE, p.getK1() * p.getK2() + p.getV());
                        found += dspKernel.save(writer);
                        dspKernel.dispose();
                    }
                }
            }

        } else {
            // 迭代集合E的所有可能组合
            if (p.getE() == p.getK1() && p.getE() == p.getK2()) {
                for (int[] eSetIdx : new Combinations(p.getV(), p.getE())) {
                    boolean[] remainderSetIdx1 = remainderSetIdx.clone();
                    byte[] eSet = ArrayHandler.extractCombElementB(remainderSetIdx1, eSetIdx);

                    ArrayHandler.fillToArray(k1SetAllComb, eSet, combIdx);
                    ArrayHandler.fillToArray(k2SetAllComb, eSet, combIdx);
                    combIdx++;

                    // 开始分段
                    if (combIdx == MAX_VALUE) {
                        combIdx = 0; // 归零
                        DspKernel dspKernel = new DspKernel(k1SetAllComb, k2SetAllComb, p, MAX_VALUE);
                        dspKernel.execute(MAX_VALUE, p.getK1() * p.getK2() + p.getV());
                        found += dspKernel.save(writer);
                        dspKernel.dispose();
                    }
                }
            } else if (p.getE() == p.getK2() && p.getE() != p.getK1()) {
                for (int[] eSetIdx : new Combinations(p.getV(), p.getE())) {
                    boolean[] remainderSetIdx1 = remainderSetIdx.clone();
                    byte[] eSet = ArrayHandler.extractCombElementB(remainderSetIdx1, eSetIdx);

                    for (int[] k1Set_eSetIdx : new Combinations(p.getV() - p.getK2(), p.getK1() - p.getE())) {
                        boolean[] remainderSetIdx2 = remainderSetIdx1.clone();
                        byte[] k1Set_eSet = ArrayHandler.extractCombElementB(remainderSetIdx2, k1Set_eSetIdx);
                        byte[] k1Set = ArrayHandler.concatB(k1Set_eSet, eSet);

                        ArrayHandler.fillToArray(k1SetAllComb, k1Set, combIdx);
                        ArrayHandler.fillToArray(k2SetAllComb, eSet, combIdx);
                        combIdx++;

                        // 开始分段
                        if (combIdx == MAX_VALUE) {
                            combIdx = 0; // 归零
                            DspKernel dspKernel = new DspKernel(k1SetAllComb, k2SetAllComb, p, MAX_VALUE);
                            dspKernel.execute(MAX_VALUE, p.getK1() * p.getK2() + p.getV());
                            found += dspKernel.save(writer);
                            dspKernel.dispose();
                        }
                    }
                }
            } else if (p.getE() == p.getK1() && p.getE() != p.getK2()) {
                for (int[] eSetIdx : new Combinations(p.getV(), p.getE())) {
                    boolean[] remainderSetIdx1 = remainderSetIdx.clone();
                    byte[] eSet = ArrayHandler.extractCombElementB(remainderSetIdx1, eSetIdx);

                    for (int[] k2Set_eSetIdx : new Combinations(p.getV() - p.getK1(), p.getK2() - p.getE())) {
                        boolean[] remainderSetIdx2 = remainderSetIdx1.clone();
                        byte[] k2Set_eSet = ArrayHandler.extractCombElementB(remainderSetIdx2, k2Set_eSetIdx);
                        byte[] k2Set = ArrayHandler.concatB(k2Set_eSet, eSet);

                        ArrayHandler.fillToArray(k1SetAllComb, eSet, combIdx);
                        ArrayHandler.fillToArray(k2SetAllComb, k2Set, combIdx);
                        combIdx++;

                        // 开始分段
                        if (combIdx == MAX_VALUE) {
                            combIdx = 0; // 归零
                            DspKernel dspKernel = new DspKernel(k1SetAllComb, k2SetAllComb, p, MAX_VALUE);
                            dspKernel.execute(MAX_VALUE, p.getK1() * p.getK2() + p.getV());
                            found += dspKernel.save(writer);
                            dspKernel.dispose();
                        }
                    }
                }
            } else {
                for (int[] eSetIdx : new Combinations(p.getV(), p.getE())) {
                    boolean[] remainderSetIdx1 = remainderSetIdx.clone();
                    byte[] eSet = ArrayHandler.extractCombElementB(remainderSetIdx1, eSetIdx);
                    for (int[] k2Set_eSetIdx : new Combinations(p.getV() - p.getE(), p.getK2() - p.getE())) {
                        boolean[] remainderSetIdx2 = remainderSetIdx1.clone();
                        byte[] k2Set_eSet = ArrayHandler.extractCombElementB(remainderSetIdx2, k2Set_eSetIdx);
                        byte[] k2Set = ArrayHandler.concatB(k2Set_eSet, eSet);

                        for (int[] k1Set_eSetIdx : new Combinations(p.getV() - p.getK2(), p.getK1() - p.getE())) {
                            boolean[] remainderSetIdx3 = remainderSetIdx2.clone();
                            byte[] k1Set_eSet = ArrayHandler.extractCombElementB(remainderSetIdx3, k1Set_eSetIdx);
                            byte[] k1Set = ArrayHandler.concatB(k1Set_eSet, eSet);

                            ArrayHandler.fillToArray(k1SetAllComb, k1Set, combIdx);
                            ArrayHandler.fillToArray(k2SetAllComb, k2Set, combIdx);
                            combIdx++;

                            // 开始分段
                            if (combIdx == MAX_VALUE) {
                                combIdx = 0; // 归零
                                DspKernel dspKernel = new DspKernel(k1SetAllComb, k2SetAllComb, p, MAX_VALUE);
                                dspKernel.execute(MAX_VALUE, p.getK1() * p.getK2() + p.getV());
                                found += dspKernel.save(writer);
                                dspKernel.dispose();
                            }
                        }
                    }
                }
            }
        }

        // 剩余部分
        DspKernel dspKernel = new DspKernel(k1SetAllComb, k2SetAllComb, p, combIdx);
        dspKernel.execute(combIdx, p.getK1() * p.getK2() + p.getV());
        found += dspKernel.save(writer);
        dspKernel.dispose();

        return found;
    }
}
